﻿using RazorEngine;
using RazorEngine.Templating;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;

namespace Lrd.Template.Core.CodeGenerator
{
  public  class RazorCodeGenerator: ICodeGenerator
    {

        private readonly IDictionary<CodeType, ITemplateKey> _keyDict = new ConcurrentDictionary<CodeType, ITemplateKey>();
        private readonly IDbServices _dbServices = null;
        public RazorCodeGenerator(IDbServices dbServices)
        {
            _dbServices = dbServices;
        }

        private readonly List<DbTable> _dbTables = new List<DbTable>();
        private  ProjectMetadata _projectMetadata = null;

        /// <summary>
        /// 如果为空说明ceneratorTableName要生成所有表
        /// </summary>
        /// <param name="projectMetadata"></param>
        /// <param name="ceneratorTableName"></param>
        /// <returns></returns>
        public CodeFile[] GenerateProjectCode(ProjectMetadata projectMetadata,string[] ceneratorTableNames)
        {
            _projectMetadata = projectMetadata;
            List<CodeFile> codeFiles = new List<CodeFile>();
            var dbTables = _dbServices.GetGetDbTableAndColumns(ceneratorTableNames);

            _dbTables.AddRange(dbTables);
            codeFiles.AddRange(dbTables.Select(GenerateEntityCode));
            codeFiles.AddRange(dbTables.Select(GenerateEntityConfigurationCode));
            return codeFiles.OrderBy(o=>o.FileName).ToArray();
        }

        public CodeFile GenerateEntityCode(DbTable table)
        {
            var code = GetTemplateCode(table,CodeType.Entity);
            var codeFile = new CodeFile()
            {
                SourceCode = code,
                FileName = $"Entity/{table.TableName}.generated.cs"
            };
            return codeFile;
        }

        private string GetTemplateCode(DbTable table, CodeType codeType)
        {
            table.Project = _projectMetadata;
            string code;
            if (!_keyDict.TryGetValue(codeType, out ITemplateKey key))
            {
                string template = GetInternalTemplate(codeType);
                key = GetKey(codeType, template);
                code = Engine.Razor.RunCompile(template, key, table.GetType(), table);
                _keyDict.Add(codeType, key);
            }
            else
            {
                code = Engine.Razor.Run(key, table.GetType(), table);
            }
            return code;
        }

        public CodeFile GenerateEntityConfigurationCode(DbTable table)
        {

         
        
            var codeFile = new CodeFile()
            {
                SourceCode = this.GetTemplateCode(table, CodeType.EntityConfiguration),
                FileName = $"EntityConfiguration/{table.TableName}Configuration.generated.cs"
            };

            return codeFile;

        }


        private ITemplateKey GetKey(CodeType codeType, string template)
        {
          

            string md5 = HashHelper.GetMd5(template);
            string name = $"{codeType.ToString()}-{md5}";
            return Engine.Razor.GetKey(name);
        }

        /// <summary>
        /// 读取指定代码类型的内置代码模板
        /// </summary>
        /// <param name="type">代码类型</param>
        /// <returns></returns>
        private string GetInternalTemplate(CodeType type)
        {
            String projectName = Assembly.GetExecutingAssembly().GetName().Name.ToString();
            string resName = $"{projectName}.CodeGenerator.Templates.{type.ToString()}.cshtml";
            Stream stream = GetType().Assembly.GetManifestResourceStream(resName);
            if (stream == null)
            {
                throw new Exception("没有找到对应的模板");
            }
            using (StreamReader reader = new StreamReader(stream))
            {
                return reader.ReadToEnd();
            }
        }

        /// <summary>
        /// 写文件
        /// </summary>
        /// <param name="fileName">文件完整路径</param>
        /// <param name="content">内容</param>
        public  void WriteAndSave(string filePath, CodeFile codeFile , bool isCoveredExsited = true)
        {
       

            var saveFilePath= $"{Path.Combine(@"{0}\{1}", filePath, codeFile.FileName)}";
            DicCreate(saveFilePath);
            //实例化一个文件流--->与写入文件相关联
            using (var fs = new FileStream(saveFilePath, FileMode.Create, FileAccess.Write))
            {
                //实例化一个StreamWriter-->与fs相关联
                using (var sw = new StreamWriter(fs))
                {
                    //开始写入
                    sw.Write(codeFile.SourceCode);
                    //清空缓冲区
                    sw.Flush();
                    //关闭流
                    sw.Close();
                    fs.Close();
                }
            }
        }

        private void DicCreate(string path)
        {
            path = Path.GetDirectoryName(path);
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }
        }

    }


    public enum CodeType
    {
        /// <summary>
        /// 实体类
        /// </summary>
        Entity,

        /// <summary>
        /// 
        /// </summary>
        EntityConfiguration,

    }
}
